package Executor;


import DataEvaluation.CommandCreator;
import DataEvaluation.DataArranger;
import DataEvaluation.RConnection;
import DataStructures.Property;
import DataSelection.QueryBuilder;
import DataSelection.QueryExecutor;
import DataSelection.TaxonomyParser;
import DataStructures.Globals;
import DataStructures.Request;
import DataStructures.ResultBean;
import DataStructures.ResultsBean;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author Manuel
 */
public class ExecutionManager {

    private RequestInterpreter interpreter;
    private TaxonomyParser taxonomyparser;
    private QueryBuilder queryBuilder;
    private QueryExecutor queryExecutor;
    private DataArranger dataArranger;
    private CommandCreator commandCreator;
    private RConnection rConnection;
    private String fileDir;
    private boolean sameConcepts;
    
    public ExecutionManager(){
        interpreter = new RequestInterpreter();
        taxonomyparser = new TaxonomyParser();
        queryBuilder = new QueryBuilder();
        queryExecutor = new QueryExecutor();
        dataArranger = new DataArranger();
        commandCreator = new CommandCreator();
        rConnection = new RConnection();
        sameConcepts = false;
    }

    public ResultsBean processQueryRequest(Request request){

        this.makeWD();

        ResultsBean results = new ResultsBean();

        //interpret the request
        String concept1 = request.getConcept1().toLowerCase();
        String concept2 = request.getConcept2().toLowerCase();
        results.setRequest(concept1 + " -> "+concept2);
        //Analyze the concepts requested by parsing the given taxonomies
        ArrayList<Property> c1 = this.taxonomyparser.parseTaxonomy(concept1);
        ArrayList<Property> c2 = this.taxonomyparser.parseTaxonomy(concept2);
        this.taxonomyparser.setNoOfConcept1Properties(c1.size());

        //Build a query for the analyzed requested concepts
        String query = queryBuilder.getQuery(c1,c2);

        //Query for the requested concepts
        ArrayList<String> values = this.queryExecutor.query(query);

         //store into file
        String valueString = "";
        for(String v:values){
            valueString+=(v+";");
        }
        String dataFile = Globals.fileDir+"/data"+(++Globals.fileNo);
        Utilities.writeToFile(valueString, dataFile, false);

        //store all values in a arraylist of arrays
        ArrayList<String[]> allvals = new ArrayList<String[]>();


        for(String value : values){
            allvals.add(value.split("-"));
        }



        //get the property pairs which have to be compared
        ArrayList<String[]> columns = dataArranger.createColumns(allvals,taxonomyparser);
        
        //get the statements which have to be sent to R
        ArrayList<String[]> commands = commandCreator.getInterMediateCommands(columns, fileDir);

        //execute the statements and get the html code for image and result text
        ArrayList<String[]> analysisResult = rConnection.executeInterMediateCommands(commands,fileDir);

        String html1 = "<table>";
        for(String[] current : analysisResult){
            html1 += "<tr>";
            for(String val : current){
                html1+=("<td>");
                    html1+=val;
                html1+=("</td>");
            }
            html1 += "</tr>";
        }
        html1 += "</table>";

        String html =  "<br/> <table border=\"5\">";
        for(String[] current : allvals){
            html += "<tr>";
            for(String val : current){
                html+=("<td>"+val+"</td>");
            }
            html += "</tr>";
        }
        html += "</table>";

        //now elaborate the query request
        for(String[] currentResult : analysisResult){
            ResultBean result = new ResultBean();
                result.setImage(currentResult[0]);
                result.setLine1(currentResult[1]);
                result.setLine2(currentResult[2]);
                result.setLine3(currentResult[3]);
                result.setLine4(currentResult[4]);
                result.setLine5(currentResult[5]);
                result.setTitle(currentResult[6]);
                results.add(true,result);
        }

        //add the text for the analysis message
        String finalmessage = this.composeTextMessage(concept1,concept2,c1,c2);
        Utilities.writeToFile(concept1+";"+taxonomyparser.getNoOfConcept1Properties()+";"+concept2+";"+finalmessage, dataFile, true);

        String intermediateMessage = this.composeInterMediateMessage();

        results.setResultText(intermediateMessage);
        results.setIntermediate(true);
        results.setDatacode(dataFile.substring(dataFile.lastIndexOf("/")+1));
        return results;

    }


    public ResultsBean processFilterRequest(HttpServletRequest request) {
        ResultsBean results = new ResultsBean();
        String code = request.getParameter("code");
        String dataFile = Globals.fileDir+"/"+code;
        this.makeWD();

        //read the values
        String valueString = Utilities.readFromFile(dataFile);
        //create an arraylist
        String[] valArray = valueString.split(";");
        ArrayList<String> valList = new ArrayList<String>();
        for(String val:valArray){
            valList.add(val);
        }
        int lastElement = valList.size()-1;
        String resultMessage = valList.get(lastElement);
        String concept2 = valList.get(lastElement-1);
        int noConcept1 = Integer.parseInt(valList.get(lastElement-2));
        String concept1 = valList.get(lastElement-3);

        valList.remove(lastElement);
        valList.remove(lastElement-1);
        valList.remove(lastElement-2);
        valList.remove(lastElement-3);

        //store all values in a arraylist of arrays
        ArrayList<String[]> allvals = new ArrayList<String[]>();


        for(String value:valList){
            allvals.add(value.split("-"));
        }

        //TODO FILTER HERE
        ArrayList<String[]> constraints = this.getConstraints(request,allvals.get(0));
        ArrayList<String[]> filteredVals = this.filterList(allvals,constraints);

        String valString="";
        for(String[] ar: filteredVals){
            for(String val:ar){
                valString+=(val+"-");
            }
            valString= valString.substring(0,valString.length()-1);
            valString+=";";
        }
        Utilities.writeToFile(valString, dataFile, false);

        //get the property pairs which have to be compared
        ArrayList<String[]> columns = dataArranger.createColumns(filteredVals,taxonomyparser);

        //get the statements which have to be sent to R
        ArrayList<String[]> commands = commandCreator.getInterMediateCommands(columns, fileDir);

        //execute the statements and get the html code for image and result text
        ArrayList<String[]> analysisResult = rConnection.executeInterMediateCommands(commands,fileDir);

        String html1 = "<table>";
        for(String[] current : analysisResult){
            html1 += "<tr>";
            for(String val : current){
                html1+=("<td>");
                    html1+=val;
                html1+=("</td>");
            }
            html1 += "</tr>";
        }
        html1 += "</table>";

        String html =  "<br/> <table border=\"5\">";
        for(String[] current : allvals){
            html += "<tr>";
            for(String val : current){
                html+=("<td>"+val+"</td>");
            }
            html += "</tr>";
        }
        html += "</table>";

        //now elaborate the query request
        for(String[] currentResult : analysisResult){
            ResultBean result = new ResultBean();
                result.setImage(currentResult[0]);
                result.setLine1(currentResult[1]);
                result.setLine2(currentResult[2]);
                result.setLine3(currentResult[3]);
                result.setLine4(currentResult[4]);
                result.setLine5(currentResult[5]);
                result.setTitle(currentResult[6]);
                results.add(true,result);
        }

        //add the text for the analysis message
        String finalmessage = resultMessage;
        Utilities.writeToFile(concept1+";"+taxonomyparser.getNoOfConcept1Properties()+";"+concept2+";"+finalmessage, dataFile, true);

        String intermediateMessage = this.composeInterMediateMessage();

        results.setResultText(intermediateMessage);
        results.setIntermediate(true);
        results.setDatacode(dataFile.substring(dataFile.lastIndexOf("/")+1));
        return results;

    }


    public ResultsBean processResultRequest(HttpServletRequest request){
        ResultsBean results = new ResultsBean();
        String code = request.getParameter("code");

        this.makeWD();

        //read the values
        String valueString = Utilities.readFromFile(Globals.fileDir+"/"+code);
        //create an arraylist
        String[] valArray = valueString.split(";");
        ArrayList<String> valList = new ArrayList<String>();
        for(String val:valArray){
            valList.add(val);
        }
        int lastElement = valList.size()-1;
        String resultMessage = valList.get(lastElement);
        String concept2 = valList.get(lastElement-1);
        int noConcept1 = Integer.parseInt(valList.get(lastElement-2));
        String concept1 = valList.get(lastElement-3);

        valList.remove(lastElement);
        valList.remove(lastElement-1);
        valList.remove(lastElement-2);
        valList.remove(lastElement-3);

        //store all values in a arraylist of arrays
        ArrayList<String[]> allvals = new ArrayList<String[]>();


        for(String value:valList){
            allvals.add(value.split("-"));
        }

        //TODO FILTER HERE
        ArrayList<String[]> constraints = this.getConstraints(request,allvals.get(0));
        ArrayList<String[]> filteredVals = this.filterList(allvals,constraints);

        //get the property pairs which have to be compared
        ArrayList<String[][]> pairs = dataArranger.getPairs(filteredVals,this.taxonomyparser,noConcept1);
        System.out.println("Size pairs, pairslength:"+pairs.size()+" "+pairs.get(0)[1].length);
        results.setResultText(results.getResultText()+"");
        //get the statements which have to be sent to R
        ArrayList<String[]> commands = commandCreator.getResultCommands(pairs, fileDir);


        //execute the statements and get the html code for image and result text
        ArrayList<String[]> analysisResult = rConnection.executeCommands(commands,fileDir);

        String html1 = "<table>";
        for(String[] current : analysisResult){
            html1 += "<tr>";
            for(String val : current){
                html1+=("<td>");
                    html1+=val;
                html1+=("</td>");
            }
            html1 += "</tr>";
        }
        html1 += "</table>";

        String html =  "<br/> <table border=\"5\">";
        for(String[] current : allvals){
            html += "<tr>";
            for(String val : current){
                html+=("<td>"+val+"</td>");
            }
            html += "</tr>";
        }
        html += "</table>";

        //now elaborate the query request
        for(String[] currentResult : analysisResult){
            ResultBean result = new ResultBean();
            if(currentResult.length == 6){
                result.setImage(currentResult[0]);
                result.setLine1(currentResult[1]);
                result.setLine2(currentResult[2]);
                result.setLine3(currentResult[3]);
                result.setLine4(currentResult[4]);
                result.setTitle(currentResult[5]);
                result.setCorrelation(true);
                results.add(true,result);
            }
            else if(currentResult.length == 2){
                result.setImage(currentResult[0]);
                result.setTitle(currentResult[1]);
                result.setCorrelation(false);
                results.add(false,result);
            }
        }

        //add the text for the analysis message
        results.setResultText(resultMessage+" on a sample of "+filteredVals.size()+" items.");
        results.setIntermediate(false);
        return results;

    }


    public ResultsBean executeRequest(Map requestParameters){
        Request request = interpreter.interpretQuery(requestParameters);
        ResultsBean resultsBean = this.processQueryRequest(request);
        return resultsBean;
    }

    public ResultsBean executeRequest(String requestString){
        Request request = interpreter.interpretQuery(requestString,taxonomyparser.getAllConcepts());

        if(request.getConcept1().equals(request.getConcept2())){
            this.sameConcepts= true;
        }
        ResultsBean resultsBean = this.processQueryRequest(request);
        return resultsBean;
    }

    private void makeWD() {
        fileDir = Globals.fileDir;
        int directoryNumber = Globals.directoryNumber;
        Globals.directoryNumber++;
        fileDir += "/files"+directoryNumber;
        File dir = new File(fileDir);
        if(!dir.exists()){
            dir.mkdirs();
        } 
    }

    public void removeWD(){
        File dir = new File(Globals.fileDir);
        File[] list = dir.listFiles();
        for(File f: list){

            if((System.currentTimeMillis()-f.lastModified())>Globals.maxImageAge){
                if(f.isDirectory())
                    this.deleteDir(f);
                else
                    f.delete();

            }
        }
    }

    private void deleteDir(File dir){

        String[] list = dir.list();

          if (list != null) {
            for (int i = 0; i < list.length; i++) {
              File entry = new File(dir, list[i]);

              if (entry.isDirectory())
              {
                    deleteDir(entry);
              }
              else
              {
                entry.delete();
              }
            }
          }
          dir.delete();
    }

    private String composeTextMessage(String concept1, String concept2, ArrayList<Property> c1, ArrayList<Property> c2) {
        HashMap attributes1 = new HashMap();
        for(Property at: c1){
            if(attributes1.containsKey(at.getConcept())){
                String cur = (String) attributes1.get(at.getConcept());
                attributes1.put(at.getConcept(), cur+","+at.getName());
            }
            else{
                attributes1.put(at.getConcept(), at.getName());
            }
        }
        HashMap attributes2 = new HashMap();
        for(Property at: c2){
            if(attributes2.containsKey(at.getConcept())){
                String cur = (String) attributes2.get(at.getConcept());
                attributes2.put(at.getConcept(), cur+","+at.getName());
            }
            else{
                attributes2.put(at.getConcept(), at.getName());
            }
        }
        String intro = ("The query was interpreted as \""+concept1+", "+concept2+"\". ");
        String notification = "<br/>";
        if(dataArranger.isRelated())
            notification = "Please note that the concepts "+concept1 +" and "+ concept2+ " are parented (i.e. one is the child of the other). The analysis in the following was carried out for distinct attributes only."+notification;
        if(this.sameConcepts)
            notification = "Please note that the concepts "+concept1 +" and "+ concept2+ " are the same. The analysis in the following was carried out for distinct attributes only."+notification;
        String c1Text = capitalize(concept1)+" is defined on the attributes ";
        ArrayList<Object> keys1 = new ArrayList<Object>(attributes1.keySet());
        for(int i = 0;i<keys1.size();i++){
            String key = (String) keys1.get(i);
            if(keys1.size() == 1)
                c1Text += (key) + " ("+(String)attributes1.get(key)+"). " ;
            else if(i == (keys1.size() - 1))
                c1Text +=  " and "+key+" ("+(String)attributes1.get(key)+"). <br/>" ;
            else
                c1Text += (key) + " ("+(String)attributes1.get(key)+"), " ;
        }
        String c2Text = capitalize(concept2)+" is defined on the attributes ";
        ArrayList<Object> keys2 = new ArrayList<Object>(attributes2.keySet());
        for(int i = 0;i<keys2.size();i++){
            String key = (String) keys2.get(i);
            if(keys2.size() == 1)
                c2Text += (key) + " ("+(String)attributes2.get(key)+"). " ;
            else if(i == (keys2.size() - 1))
                c2Text +=  " and "+key+" ("+(String)attributes2.get(key)+"). <br/>" ;
            else
                c2Text += (key) + " ("+(String)attributes2.get(key)+"), " ;
        }
        String end = ("The following analyses investigate on the effect of "+concept1+ " attributes on the attributes of "+concept2);
        String resultString = intro+notification+c1Text+c2Text+end;
        return resultString;

    }

    private static String capitalize(String s) {
        if (s.length() == 0) return s;
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }

    private String composeInterMediateMessage() {
        return "";
    }

    private ArrayList<String[]> getConstraints(HttpServletRequest request, String[] attributes) {
        Enumeration paramNames = request.getParameterNames();
        HashMap constraintMap = new HashMap();
        while(paramNames.hasMoreElements()) {
          String paramName = (String)paramNames.nextElement();
          String currentParameter = request.getParameter(paramName);
          if (currentParameter != null && !currentParameter.equals("") && paramName.length()>2) {
              String type = paramName.substring(0,3);
               String param = paramName.substring(3);
               String attribute = RequestInterpreter.checkSimilarity(param,attributes);
               if(!constraintMap.containsKey(attribute))
                  constraintMap.put(attribute, new String[3]);
               String[] constraint = (String[]) constraintMap.get(attribute);
               constraint[0] = attribute;
               if(type.equals("min") || type.contains("min"))
                   constraint[1] = currentParameter;
               else if(type.equals("max") || type.contains("max"))
                   constraint[2] = currentParameter;
              constraintMap.put(attribute, constraint);

          } 
        }
        return new ArrayList(constraintMap.values());

        
    }

    private ArrayList<String[]> filterList(ArrayList<String[]> allvals, ArrayList<String[]> constraints) {
        int initialsize = allvals.size();
        for(String[] currentConstraint: constraints){
            System.out.println(currentConstraint[0]+" min: "+currentConstraint[1]+ " max"+currentConstraint[2]);
            if(currentConstraint.length == 3 && !(currentConstraint[1] == null && currentConstraint[2] == null)){
                int positionOfElement = -1;
                for(int i = 0;i<allvals.get(0).length;i++){
                    if(allvals.get(0)[i].equals(currentConstraint[0])){
                        positionOfElement = i;
                    }
                }
                if(positionOfElement != -1){
                    for(int i = 1;i<allvals.size()-1;i++){
                        String[] current = allvals.get(i);
                        for(String val: current)
                            System.out.print(val+"-");
                        System.out.println("");
                        String sval = current[positionOfElement];

                        if(sval.contains("("))
                            sval = sval.substring(1,sval.indexOf(","));
                        
                        double value = Double.parseDouble(sval);

                        //delete if above maximum (with not null checks
                        if(currentConstraint[1] != null && !currentConstraint[1].isEmpty())
                            if(value<Double.parseDouble(currentConstraint[1]))
                                allvals.remove(current);
                        
                        //delete if below minimum (with not null checks
                        if(currentConstraint[2] != null && !currentConstraint[2].isEmpty())
                            if(value>Double.parseDouble(currentConstraint[2]))
                                allvals.remove(current);
                    }
                }
            }
        }
        System.out.println("REMOVED "+(allvals.size()-initialsize)+" VALUES");
        return allvals;
    }

}
